/*
 * Copyright 2011 Mark Davidson.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package org.dctmcommons.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.dctmcommons.IAttribHelper;
import org.dctmcommons.IParamCheck;

import com.documentum.fc.client.IDfTypedObject;
import com.documentum.fc.common.DfException;
import com.documentum.fc.common.DfLogger;
import com.documentum.fc.common.IDfAttr;
import com.documentum.fc.common.IDfId;
import com.documentum.fc.common.IDfTime;
import com.documentum.fc.common.IDfValue;

/**
 * Attribute-related helper methods.
 */
public class AttribHelper implements IAttribHelper {
	
	/** The regex used to merge attribute values into a string template. */
	private static final String INTERPOLATION_REGEX = "\\{(.+?)\\}";
	
	/** The parameter check instance. */
	private static final IParamCheck paramCheck = ParamCheck.getInstance();
	
    /**
     * Default constructor.
     */
    private AttribHelper() {}

    /**
     * Factory method.
     * @return The attribute helper instance.
     */
    public static IAttribHelper getInstance() {
        return new AttribHelper();
    }

	
	/* (non-Javadoc)
	 * @see org.dctmcommons.impl.IAttribHelper#interpolate(com.documentum.fc.client.IDfTypedObject, java.lang.String)
	 */
	@Override
	public String interpolate(final IDfTypedObject object,
							  final String template) throws DfException {
		final Pattern pattern = Pattern.compile(INTERPOLATION_REGEX);
		final Matcher matcher = pattern.matcher(template);
		final StringBuffer buffer = new StringBuffer();

		// replace tokens with appropriate attribute values
		// a example template is "{r_object_id} - {object_name}"
		while (matcher.find()) {
			// matched tokens are expected to be attribute names
		    final String attribName = matcher.group(1);
		    String replacement = null;
		    
		    DfLogger.debug(this,
		    			  "Got match: {0}",
		    			  new String[] { attribName },
		    			  null);
		    
		    if (!object.isAttrRepeating(attribName)) {
		    	replacement = object.getValue(attribName).asString();
		    	
			    DfLogger.debug(this,
			    			   "Got single attrib value: {0}",
			    			   new String[] { replacement },
			    			   null);

		    } else {
		    	final int valueCount = object.getValueCount(attribName);
		    	if (valueCount > 0) {
		    		final IDfValue value = object.getRepeatingValue(attribName,
		    														0);
		    		replacement = value.asString();

		    		DfLogger.debug(this,
				    			   "Got 1st repeating attrib value: {0}",
				    			   new String[] { replacement },
				    			   null);
		    	}
		    }
		    
		    if (replacement != null) {
		        matcher.appendReplacement(buffer, "");
		        buffer.append(replacement);
		    }
		}
		matcher.appendTail(buffer);
		return buffer.toString();
	}

	/* (non-Javadoc)
	 * @see org.dctmcommons.impl.IAttribHelper#readAttribs(com.documentum.fc.client.IDfTypedObject, java.lang.String[])
	 */
	@Override
	public Map<String, Object> readAttribs(final IDfTypedObject typedObject,
										   String[] attribNames)
        throws DfException {
		
		paramCheck.checkObject(typedObject, "typedObject");
		
		final Map<String, Object> attribs = new LinkedHashMap<String, Object>();
		
		if (attribNames == null || attribNames.length == 0) {
			attribNames = getAttribNames(typedObject);
		}
		
		if (attribNames != null) {
			for (final String attribName : attribNames) {
				final boolean isRepeating = typedObject
												.isAttrRepeating(attribName);
				
				final int dataType = typedObject.getAttrDataType(attribName);
				
				//System.out.println("Processing attrib: " + attribName);
				//System.out.println("Is repeating: " + isRepeating);
				
				switch (dataType) {
					case IDfAttr.DM_STRING:
						//System.out.println("Data type: String");
						if (!isRepeating) {
							attribs.put(attribName,
										typedObject.getString(attribName));
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<String> attribValues =
														new ArrayList<String>();
								for (int i = 0; i < numAttribValues; i++) {
									attribValues.add(typedObject
											.getRepeatingString(attribName, i));
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new String[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_INTEGER:
						//System.out.println("Data type: Integer");
						if (!isRepeating) {
							attribs.put(attribName,
										typedObject.getInt(attribName));
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<Integer> attribValues =
													new ArrayList<Integer>();
								for (int i = 0; i < numAttribValues; i++) {
									attribValues.add(typedObject
											.getRepeatingInt(attribName, i));
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new Integer[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_BOOLEAN:
						//System.out.println("Data type: Boolean");
						if (!isRepeating) {
							attribs.put(attribName,
										typedObject.getBoolean(attribName));
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<Boolean> attribValues =
													new ArrayList<Boolean>();
								for (int i = 0; i < numAttribValues; i++) {
									attribValues.add(typedObject
										.getRepeatingBoolean(attribName, i));
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new Boolean[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_TIME:
						//System.out.println("Data type: Time");
						if (!isRepeating) {
							final IDfTime time = 
										typedObject.getTime(attribName);
							final Date date = (time != null) ?
													time.getDate() :
														null;
							attribs.put(attribName, date);
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<Date> attribValues =
													new ArrayList<Date>();
								for (int i = 0; i < numAttribValues; i++) {
									final IDfTime time = typedObject
											.getRepeatingTime(attribName, i);
									final Date date = (time != null) ?
											time.getDate() :
												null;
									attribValues.add(date);
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new Date[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_DOUBLE:
						//System.out.println("Data type: Double");
						if (!isRepeating) {
							attribs.put(attribName,
										typedObject.getDouble(attribName));
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<Double> attribValues =
														new ArrayList<Double>();
								for (int i = 0; i < numAttribValues; i++) {
									attribValues.add(typedObject
											.getRepeatingDouble(attribName, i));
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new Double[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_ID:
						//System.out.println("Data type: ID");
						if (!isRepeating) {
							final IDfId id = typedObject.getId(attribName);
							final String strId = (id == null) ?
													null :
														id.getId();
							attribs.put(attribName, strId);
						} else {
							final int numAttribValues = typedObject
																.getValueCount(attribName);
							if (numAttribValues > 0) {
								final List<String> attribValues =
														new ArrayList<String>();
								for (int i = 0; i < numAttribValues; i++) {
									final IDfId id = typedObject
											.getRepeatingId(attribName, i);
									final String strId = (id == null) ?
														null :
															id.getId();
									attribValues.add(strId);
								}
								
								attribs.put(attribName,
											attribValues.toArray(
															new String[] {}));
							} else {
								attribs.put(attribName, null);
							}
						}
						break;
					case IDfAttr.DM_UNDEFINED:
						break; // ignore undefined data type
				}
				
				//System.out.println("Got value: " + attribs.get(attribName));
			}
		}
		
		return attribs;
	}
	
	/* (non-Javadoc)
	 * @see org.dctmcommons.impl.IAttribHelper#getAttribNames(com.documentum.fc.client.IDfTypedObject)
	 */
	@Override
	public String[] getAttribNames(final IDfTypedObject typedObject)
		throws DfException {
		
		paramCheck.checkObject(typedObject, "typedObject");
		
		final List<String> attribNames = new ArrayList<String>();
		final int numAttribs = typedObject.getAttrCount();
		
		for (int i = 0; i < numAttribs; i++) {
			attribNames.add(typedObject.getAttr(i).getName());
		}
		
		if (attribNames.size() == 0) {
			return null;
		} else {
			return attribNames.toArray(new String[] {});
		}
	}
}
